//
//  APIHeartRateImp.swift
//  APIHeartRate_Example
//
//  Created by Tek on 2023/1/5.
//  Copyright © 2023 CocoaPods. All rights reserved.
//

import CoreBluetooth
import Foundation

public enum FetchDataType {
    case heartRate
    case steps
    case oxygen
    case pressure
    case calorie
    case sleep
    case excercise
    case motionDaily
    case motionSport
}

enum TrasitionState {
    case initail
    case receiveTotalLength
    case receiveTimeStamp
    case inprogress
    case finish
}

class APIHeartRateImp: HRAvailabilityObserver, HRCentralDelegate {
    public static let versionToCompare = "G2.9.1"
    internal var observers: [String: APIHeartRateObserver] = [:]
    internal var remotePeripheral: BleDevice?
    internal var central = HRCentral()
    internal var logDisabled = false
    internal var dataMgt: DataAccessManager = DataAccessManager.instance
    internal var dataTotal: Int = 0
    internal var currentPage:Int = 0
    internal var dataType: FetchDataType = .heartRate
    
    internal var startDate: Date = Date()
    internal var endDate: Date = Date()
    internal var pageSizeReq = 0
    internal var state:  TrasitionState = .initail
    internal var showRawData = false
    internal var isLegacyVersion = true
    
    internal var currentCmd: UInt8 = 0x00
    internal var deviceInfos: [String: Any] = [:]
    internal var cursorInfos: [String: Int] = [:]
    
    internal var loggerInternal: APILogger = APILogger()
    internal var isClickReset = false
    
    init() {
        central.delegate = self
        central.addAvailabilityObserver(self)
        HROTAManager.share.setOtaDelegate(del: self)
        do {
            try central.startWithConfiguration()
        } catch {}
    }
    
    func setLogger(impl: APILoggerRepository, level: Int) {
        loggerInternal.setLogger(impl: impl, level: level)
    }
    
    func setLoggerLevel(level: Int) {
        loggerInternal.setLogLevel(level: level)
    }

    func disableLog(_ status: Bool) {
        logDisabled = status
    }

    func registerBleListener(listener: APIHeartRateObserver) {
        let clsName = String(describing: listener.self)
        observers[clsName] = listener
    }

    func unregisterBleListener(listener: APIHeartRateObserver) {
        let clsName = String(describing: listener.self)
        observers.removeValue(forKey: clsName)
    }

    func connectBluetooth(device: BleDicoveryDevice) {
        logStr(log: "开始连接")
        loggerInternal.i(info: "connectBluetooth(device: \(device)")
        self.observers.forEach({ _, delegateApi in
            delegateApi.bleConnectStatus(status: .connecting, device: device.remotePeripheral)
        })
        central.connect(remotePeripheral: device.remotePeripheral) { [weak self] peripheral, e in
            guard let err = e else {
                self?.handleConnectionResult(remote: peripheral, error: nil)
                return
            }
            self?.loggerInternal.i(info: "connectBluetooth failed : \(err.localizedDescription)")
            self?.logStr(log: "连接失败\(err.localizedDescription)")
            self?.observers.forEach({ _, delegateApi in
                delegateApi.bleConnectError(error: err, device: device.remotePeripheral)
            })
        }
    }

    func getConnectedDevice(uuid: [UUID]) -> [BleDicoveryDevice] {
        loggerInternal.i(info: "getConnectedDevice(uuid: \(uuid)")
        let ret = central.retrieveRemotePeripheralsWithUUIDs(remoteUUIDs: uuid)
        guard let remotes = ret else { return [] }
        var temp: [BleDicoveryDevice] = []
        for item in remotes {
            temp.append(BleDicoveryDevice(advertisementData: [:], remotePeripheral: item, RSSI: 90, macAddress: item.macAddress, name: item.name))
        }
        return temp
    }

    func connectDeviceBy(deviceId: String) {
        loggerInternal.i(info: "connectDeviceBy(deviceId: \(deviceId)")
        if let uuid = UUID(uuidString: deviceId), let remote = central.retrieveRemotePeripheralWithUUID(remoteUUID: uuid) {
            
            self.observers.forEach({ _, delegateApi in
                delegateApi.bleConnectStatus(status: .connecting, device: remote)
            })
            
            central.connect(remotePeripheral: remote) { [weak self] peripheral, errConnect in
                self?.handleConnectionResult(remote: peripheral, error: errConnect)
            }
        } else {
            autoConnectScan(deviceId: deviceId)
        }
    }

    internal func autoConnectScan(deviceId: String) {
        central.scanWithDuration { _ in
        } completionHandler: { [weak self] result, _ in
            guard let res = result, let device = res.filter({ $0.deviceId == deviceId }).first else {
                self?.observers.forEach({ _, delegateApi in
                    delegateApi.bleConnectError(error: .deviceNotFound, device: nil)
                })
                return
            }
            self?.central.connect(remotePeripheral: device.remotePeripheral) { remotePeripheral, errConnect in
                self?.handleConnectionResult(remote: remotePeripheral, error: errConnect)
            }
        }
    }

    internal func handleConnectionResult(remote: BleDevice, error: BleConnectError?) {
        guard let e = error else {
            logStr(log: "连接成功: \(remote.macAddress)")
            remotePeripheral = remote
            remotePeripheral?.delegate = self
            remotePeripheral?.peripheralDelegate = self
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
                self.readFirmawareInternal()
                self.getMacAddress()
                self.syncTime()
            }
            return
        }
        observers.forEach({ _, delegateApi in
            delegateApi.bleConnectError(error: e, device: nil) })
    }

    func disconnectDevice() {
        loggerInternal.i(info: "disconnectDevice()")
        guard let remote = remotePeripheral else {
            observers.forEach({ _, delegateApi in
                delegateApi.bleConnectError(error: .deviceNotFound, device: nil) })
            return
        }
        observers.forEach({ _, delegateApi in
            delegateApi.bleConnectStatus(status: .disconnecting, device: remotePeripheral) })
        do {
            try central.disconnectRemotePeripheral(remote)
        } catch let e {
            observers.forEach({ _, delegateApi in
                delegateApi.bleConnectError(error: .failedToDisconnect(underlyingError: e), device: remote) })
        }
    }

    // MARK: OTA-----part

    func writeOTAData(_ data: Data) {
        loggerInternal.d(info: "writeOTAData()")
        guard let remote = remotePeripheral else {
            observers.forEach({ _, delegateApi in
                delegateApi.bleCommonError(error: BleCommonError.devieNotConnected) })
            return
        }
        let result = central.sendOTAData(data, toRemotePeer: remote)
        if !result {
            observers.forEach({ _, delegateApi in
                delegateApi.bleCommonError(error: BleCommonError.serviceNotFound) })
        }
    }

    func readOTAData() {
        loggerInternal.d(info: "readOTAData()")
        guard let remote = remotePeripheral else {
            observers.forEach({ _, delegateApi in
                delegateApi.bleCommonError(error: BleCommonError.devieNotConnected) })
            return
        }
        let result = central.readOTAData(toRemotePeer: remote)
        if !result {
            observers.forEach({ _, delegateApi in
                delegateApi.bleCommonError(error: BleCommonError.serviceNotFound) })
        }
    }

    func checkCharacteristic(service: CBService, targetChar: CBUUID) -> CBCharacteristic? {
        if let chars = service.characteristics {
            for char in chars {
                if char.uuid.isEqual(targetChar) {
                    return char
                }
            }
        }
        return nil
    }

    // MARK: Central Delegate

    func central(_ central: HRCentral, remotePeripheralDidDisconnect peripheral: BleDevice) {
        observers.forEach({ _, delegateApi in
            delegateApi.bleConnectStatus(status: .disconnected, device: remotePeripheral) })
        remotePeripheral = nil
    }

    func availabilityObserver(_ availabilityObservable: HRAvailabilityObservable, availabilityDidChange availability: BleAvailability) {
        observers.forEach({ _, delegateApi in
            delegateApi.bleAvailability(status: availability) })
    }

    func availabilityObserver(_ availabilityObservable: HRAvailabilityObservable, unavailabilityCauseDidChange unavailabilityCause: BleUnavailabilityCause) {
    }

    private func logStr(log: String) {
        if logDisabled {
            return
        }
        observers.forEach({ _, delegateApi in
            delegateApi.bleConnectLog(logString: "===== APIHearRate==== :" + log, device: remotePeripheral)
        })
    }
    
    internal func dataToHex(dataBuffer: Data) -> String {
        let str = dataBuffer.map { String.init(format: "%02x", $0) }.joined(separator: " ")
        return "[\(str)]"
    }
}
